#-*- coding: utf-8 -*-

import gtk
from Simulinux.Core.Diagram import Diagram
from Simulinux.Core.Blocks.Step import Step
from Simulinux.Core.Net import Net
from Simulinux.Commands import *
from Constants import *
from View import View
from NetView import NetView
from Blocks.SourcesStepView import SourcesStepView
from Blocks.MathGainView import MathGainView


class DiagramView(View):
    class MouseState:
        button = None
        times = None
        coordsi = None
        coordsf = None
        
    def __init__(self, gui, drawing):
        self.__gui = gui
        self.__drawing = drawing
        self.__drawing.drag_dest_set(
            gtk.DEST_DEFAULT_ALL,
            [('x-application/simulinux-part', gtk.TARGET_SAME_APP, 1)],
            gtk.gdk.ACTION_COPY
        )
        self.__mode = MODE_NORMAL
        self.__last_mode = MODE_NORMAL
        self.__mouse_state = self.MouseState()
        self.__last_valid_x = None
        self.__last_valid_y = None
        self.__current_pin_view = None
        self.__new_net = None
        self.context = None
        self.views = []
        self.diagram = Diagram()
    
    #Services provided
    
    def add_view(self, view):
        self.views.append(view)
    
    def remove_view(self, view):
        self.views.remove(view)
    
    def get_ui_builder(self):
        return self.__gui.get_builder()
    
    def redraw(self, *data):
        """Forces redrawing"""
        x, y, w, h = self.__drawing.allocation
        self.__drawing.queue_draw_area(x, y, x + w, y + h)
        self.__drawing.grab_focus()
    
    # Events related

    def expose(self, widget, event):
        self.context = widget.window.cairo_create()
        self.context.rectangle(event.area.x, event.area.y,
                           event.area.width, event.area.height)
        self.context.clip()
        self.context.set_line_width(2)
        self.__draw_background()
        for v in self.views:
            v.draw(self.context)
        #optionally draw the retangular_selection
        if self.__mode == MODE_REGION and \
           self.__mouse_state.coordsi and \
           self.__mouse_state.coordsf:
            self.__draw_retangular_selection()
        '''if self.diagram.new_element:
            self.__draw_new_element()'''
    
    def drag_data_received(self, widget, context, x, y, selection, 
       targetType, time):
        cmd = CmdAddBlock(self, self.diagram, selection.data, x, y)
        self.__gui.execute_command(cmd)
        self.__drawing.grab_focus()
    
    def button_pressed(self, widget, event):
        """Button pressed event."""
        #@todo: Verify possibility of defining Strategies for each mode.
        self.__drawing.grab_focus()
        self.__mouse_state.button = event.button
        self.__mouse_state.type = event.type
        self.__mouse_state.coordsi = (event.x, event.y)
        self.__last_valid_x = event.x
        self.__last_valid_y = event.y
        who = self.who_at_position(event.x, event.y)
        if self.get_mode() == MODE_NORMAL:
            #Click without SHIFT
            if not (event.state & gtk.gdk.SHIFT_MASK):
                if who is None: # Blank region clicked
                    self.switch_mode(MODE_REGION)
                    self.unselect_all()
                else: # Object clicked
                    #Object single clicked
                    if event.type == gtk.gdk.BUTTON_PRESS:
                        if not who.is_selected():
                            self.unselect_all()
                            who.select()
                        #Saving all object current positions
                        for v in [sv for sv in self.views if sv.is_selected()]:
                            v.save_current_position()
                        self.switch_mode(MODE_MOVE)
                self.redraw()
            #Shift click
            else:
                if who:
                    if who.is_selected():
                        who.discard_position_backup()
                        who.unselect()
                    else:
                        who.select()
                        for v in [sv for sv in self.views if sv.is_selected()]:
                            v.save_current_position()
                        self.switch_mode(MODE_MOVE)
            self.redraw()
        elif self.get_mode() == MODE_REGION: pass
        elif self.get_mode() == MODE_MOVE:
            #Object double clicked
            if event.type == gtk.gdk._2BUTTON_PRESS:
                 who.edit_properties()
        elif self.get_mode() == MODE_NET:
            net = Net()
            self.__new_net = NetView(net)
            self.__new_net.connect_origin_pin(self.__current_pin_view)
        elif self.get_mode() == MODE_NET_EDIT: pass
        return True

    def button_released(self, widget, *data):
        #self.clear_backup_position()
        if self.get_mode() == MODE_NORMAL: pass
        elif self.get_mode() == MODE_REGION:
            if self.__mouse_state.coordsf:
                initial = self.__mouse_state.coordsi
                final = self.__mouse_state.coordsf
                for v in self.views:
                    v.discard_position_backup()
                    if v.contains_my_region(initial, final):
                        v.select()
            #self._event_handler.clear_backup_position()
            self.switch_mode(MODE_NORMAL)
            self.redraw()
        elif self.get_mode() == MODE_MOVE:
            x0 = self.__mouse_state.coordsi[0]
            x1 = self.__last_valid_x
            y0 = self.__mouse_state.coordsi[1]
            y1 = self.__last_valid_y
            if( (x1 - x0) > 0 or (y1 - y0) > 0):
                cmd = CmdMoveSelection(self, self.views, x0, x1, y0, y1)
                self.__gui.execute_command(cmd)
            self.switch_mode(MODE_NORMAL)
        elif self.get_mode() == MODE_NET: pass
        elif self.get_mode() == MODE_NET_EDIT: pass
        self.__mouse_state.button = None
        self.__mouse_state.times = None
        self.__mouse_state.coordsi = None
        self.__mouse_state.coordsf = None

    def mouse_moved(self, widget, event):
        self.__mouse_state.coordsf = (event.x, event.y)
        if self.get_mode() == MODE_NORMAL:
            for v in self.views:
                if v.get_type() == 'block':
                    self.__current_pin_view = v.get_pin_view(event.x, event.y)
                    if self.__current_pin_view:
                        self.__gui.change_cursor('crosshair')
                        self.switch_mode(MODE_NET)
                        break
                elif v.get_type() == 'net':
                    pass
        elif self.get_mode() == MODE_REGION:
            self.redraw()
        elif self.get_mode() == MODE_MOVE:
            x0 = self.__mouse_state.coordsi[0]
            x1 = self.__mouse_state.coordsf[0]
            y0 = self.__mouse_state.coordsi[1]
            y1 = self.__mouse_state.coordsf[1]
            dx = x1 - x0
            dy = y1 - y0
            all_in = True
            for v in [sv for sv in self.views if sv.is_selected()]:
                #@todo: amarrar as nets aos blocos, de forma que se movo um 
                #bloco associado a uma net ela se desloque junto com o bloco
                #se a net já "comunica" com o bloco, isto fica automático
                newx = v.get_position_backup()[0] + dx
                newy = v.get_position_backup()[1] + dy
                if not self.in_my_region(newx, newy, v.width, v.height):
                    all_in = False
                    break
                else:
                    v.prepare_move(newx, newy)
            if all_in:
                for v in [sv for sv in self.views if sv.is_selected()]:
                    v.move()
                self.__last_valid_x = self.__mouse_state.coordsf[0]
                self.__last_valid_y = self.__mouse_state.coordsf[1]
            self.redraw()
        elif self.get_mode() == MODE_NET:
            if not self.__new_net:
                if not self.__current_pin_view.is_my_region(event.x, event.y):
                    self.__current_pin_view = None
                    self.__gui.change_cursor('normal')
                    self.switch_mode(MODE_NORMAL)
            else:
                for v in [bv for bv in self.views if bv.get_type() == 'block']:
                    dest_pin_candidate = v.get_pin_view(event.x, event.y)
                    if dest_pin_candidate:
                        self.__new_net.connect_destination_pin(dest_pin_candidate)
                        self.redraw()
                        return
                self.__new_net.connect_destination_point(event.x, event.y)
                self.redraw()
        elif self.get_mode() == MODE_NET_EDIT:
            #ESC : cancel net creation
            pass
    
    def key_pressed(self, widget, event):
        key = self.__gui.get_key_string(event)
        if self.get_mode() == MODE_NORMAL:
            if key == 'Delete':
                cmd = CmdRemoveSelected(self, self.views)
                self.__gui.execute_command(cmd)
            elif key == 'Escape':
                self.unselect_all()
            elif key == '<CTRL>a':
                self.select_all()
            elif key == '<CTRL>z':
                self.__gui.undo()
            elif key == '<CTRL><SHIFT>Z':
                self.__gui.redo()
        elif self.get_mode() == MODE_REGION:
            if key == 'Escape':
                self.switch_mode(MODE_NORMAL)
                self.redraw()
        elif self.get_mode() == MODE_MOVE: pass
        elif self.get_mode() == MODE_NET: pass
        elif self.get_mode() == MODE_NET_EDIT: pass
        
    
    # Mode related
    
    def get_mode(self):
        return self.__mode
    
    def switch_mode(self, newmode):
        self.__last_mode = self.__mode
        self.__mode = newmode
        if newmode == MODE_REGION:
            self.__mouse_state.coordsf = self.__mouse_state.coordsi
    
    def switch_previous_mode(self):
        self.__mode = self.__last_mode
        

    # Selection related

    def select_all(self):
        for v in self.views:
            v.select()
        self.redraw()

    def unselect_all(self):
        for v in self.views:
            v.unselect()
        self.redraw()
        
    
    #Position and region related
    
    def who_at_position(self, posx, posy):
        for v in self.views:
            if v.is_my_region(posx, posy):
                return v
        return None

    def in_my_region(self, xp, yp, wp, hp):
        x, y, w, h = self.__drawing.allocation
        if (xp > 0) and (yp > 0) and (xp + wp < w) and (yp + hp< h):
            return True
        else:
            return False
    
    #Private methods
        
    def __draw_background(self):
        """Fill the screen with white color"""
        rect = self.__drawing.allocation
        self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
        self.context.set_source_rgba(1, 1, 1, 1)
        self.context.fill()
        self.context.set_source_rgba(0, 0, 0, 1)

    def __draw_new_element(self):
        """Draw new element preview (new block, new net)"""
        self.diagram.new_element.draw(self.context)

    def __draw_retangular_selection(self):
        """Draw user-defined rectangular selection"""
        self.context.set_source_rgb(0.5, 0.5, 0.5)
        self.context.set_line_width(1)
        self.context.set_dash([4, 2], 2)
        self.context.rectangle(
          self.__mouse_state.coordsi[0],
          self.__mouse_state.coordsi[1],
          self.__mouse_state.coordsf[0] - self.__mouse_state.coordsi[0],
          self.__mouse_state.coordsf[1] - self.__mouse_state.coordsi[1]
        )
        self.context.stroke()
        self.context.set_dash([], 0)
        self.context.set_source_rgb(0, 0, 0)
        self.context.set_line_width(2)